#include<iostream>
#include<cerrno>
#include<string>
//套接字常用的四个头文件
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
#define NUM 1024

const uint16_t port = 8080;

int main()
{
    //1 创建套接字，打开网络文件 
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    //AF_INET：IPv4 网络协议的套接字类型
    //SOCK_DGRAM：数据报式套接 UDP
    if(sock < 0)
    {
        cout << "socket creat error:" << errno << endl;
        return 1;
    }
    //2 给该服务器绑定端口和IP
    //  先给sockaddr_in结构体填充数据
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(port);//此处的端口号，是我们计算机上的变量，
                                 //是主机序列，要转成网络字节序
    //版本一(有问题)：local.sin_addr.s_addr = "123.7.8.9"
    //问题："123.7.8.9"是点分十进制字符串风格[0,255][0,255][0,255][0,255],
    //      用4个字节就能存下，没必要这么写，所以需要作以下两个工作：
    //          a.将人识别的点分十进制字符串风格的IP地址，转化为四字节整数IP
    //          b.考虑大小端，转为网络字节序
    //版本二(有缺点)：local.sin_addr.s_addr = inet_addr("123.7.8.9");
    //               调inet_addr接口，可以完成上面a、b两个工作
    //缺点： 首先，云服务器不允许用户直接bind公网IP，
    //       另外，实际上正常编写时，我们也不会指明IP，正常写法如下：
    local.sin_addr.s_addr = INADDR_ANY;
    //INADDR_ANY：指定地址为0.0.0.0的地址(宏值为0)，表示“所有地址”或“任意地址” 
    //为什么我们不指定一个明确的IP而是设为INADDR_ANY？
    //答:如果你bind的是确定的IP(主机)，意味着只有发到该IP主机上面的数据才会交给
    //   你的网络进程，但是，一般服务器可能有多张网卡，配置多个IP，我们需要的不
    //   是某个IP上面的数据，我们需要的是，所有发送到该主机，发送到该端口的数据
    if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        cout << "bind error:" << errno << endl;
        return 2;
    }
    //3 提供服务
    bool quit = false;
    char buffer[NUM];

    while(!quit)//服务器都是死循环
    {
        //UDP读不能以普通文件的方式读
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        //接收数据报
        recvfrom(sock, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);
        //打印收到的消息
        cout << "client#" << buffer << endl;
        //发送消息给客户端
        string echo_hello = "hello";
        sendto(sock, echo_hello.c_str(), echo_hello.size(), 0, (struct sockaddr*)&peer, len);
    }
    return 0;
}
